<template>
  <div class="app-container">
    <el-card>
      <template #header>
        <div class="card-header">
          <span>分类管理</span>
        </div>
      </template>
      <el-row>
        <el-col :span="4">
          <el-switch v-model="draggable" active-text="开启拖拽" inactive-text="关闭拖拽" />
        </el-col>
        <el-col :span="20">
          <el-button type="danger" size="small" @click="handleBatchDelete">批量删除</el-button>
        </el-col>
      </el-row>
      <!-- 树形表格 -->
      <!-- 
        default-expand-all 是否默认展开所有节点 false默认不展开
        expand-on-click-node 是否在点击节点的时候展开或者收缩节点， 默认值为 true，如果为 false，则只有点箭头图标的时候才会展开或者收缩节点
        show-checkbox 是否显示节点的复选框 默认false
        node-key="catId" 树节点的数据属性， 默认为 id
        default-expanded-keys 默认展开的节点的 key 的数组 设置需要默认展开的节点
        draggable 是否开启拖拽
        allow-drag 判断节点能否被拖拽 如果返回 false ，节点不能被拖动
        allow-drop 函数(draggingNode, dropNode, AllowDropType)，三个参数，当前正在拖动的节点，目标节点，第三个参数为放置位置('prev'、'inner' 和 'next')
        @node-drop 节点被拖拽成功时调用
        -->
      <el-tree
        ref="categoryTree"
        v-loading="loading"
        style="max-width: 1000px"
        :data="treeData"
        node-key="catId"
        :props="props"
        :default-expand-all="false"
        :expand-on-click-node="true"
        show-checkbox
        :default-expanded-keys="expandedKeys"
        :draggable="draggable"
        :allow-drop="allowDrop"
        @node-drop="handleDrop"
      >
        <template #default="{ node, data }">
          <div class="custom-tree-node">
            <!-- 节点名称 -->
            <span>{{ node.label }}</span>
            <div>
              <el-button v-if="node.level <= 2" type="primary" link @click.stop="append(data)">
                添加
              </el-button>
              <el-button
                v-if="node.childNodes.length === 0"
                style="margin-left: 4px"
                type="danger"
                link
                @click.stop="remove(node, data)"
              >
                删除
              </el-button>
              <el-button style="margin-left: 4px" type="warning" link @click.stop="edit(data)">
                修改
              </el-button>
            </div>
          </div>
        </template>
      </el-tree>

      <!-- 对话框组件 -->
      <el-dialog
        v-model="dialogVisible"
        :title="titleMsg"
        width="500"
        :close-on-click-modal="false"
      >
        <el-form
          ref="ruleFormRef"
          :model="category"
          label-width="auto"
          style="max-width: 600px"
          :rules="rules"
        >
          <el-form-item label="分类名称" prop="name">
            <el-input v-model="category.name" placeholder="请输入分类名称"></el-input>
          </el-form-item>
          <el-form-item label="图标" prop="icon">
            <SelectIcon v-model="category.icon" />
          </el-form-item>
          <el-form-item label="计量单位" prop="productUnit">
            <el-input v-model="category.productUnit"></el-input>
          </el-form-item>
        </el-form>
        <template #footer>
          <div class="dialog-footer">
            <el-button @click="dialogVisible = false">关闭</el-button>
            <el-button type="primary" @click="submitData"> 确定 </el-button>
          </div>
        </template>
      </el-dialog>
    </el-card>
  </div>
</template>

<script setup>
import { onMounted, ref, reactive } from 'vue'
import SelectIcon from '@/components/common/select-icon.vue'
import {
  categoryList,
  removeCategory,
  insertCategory,
  updateCategory,
  getCategory,
  batchUpdate,
  batchCategory
} from '@/api/category.js'
import { ElMessage, ElMessageBox } from 'element-plus'
//树形数据
const treeData = ref([])
//默认展开的节点的 key 的数组
const expandedKeys = ref([])
// 树形组件绑定的key的名称
const props = {
  label: 'name',
  children: 'children'
}
// 对话框状态
const dialogVisible = ref(false)
//表单数据
const category = ref({
  name: '',
  parentCid: 0,
  catLevel: 0,
  showStatus: 0, //是否显示 0-显示 1-不显示
  sort: 0,
  icon: '',
  productUnit: '',
  productCount: 0
})
//对话框title提示信息文字
const titleMsg = ref('')
//对话框类型:作用用来提交数据时候，区分是修改还是添加
const dialogType = ref('')
//加载状态
const loading = ref(false)
//是否开启拖拽 false默认不可拖拽
const draggable = ref(false)
const ruleFormRef = ref(null)
/**
 * 表单验证规则
 */
const rules = reactive({
  name: [{ required: true, message: '请输入分类名称', trigger: 'blur' }],
  icon: [{ required: true, message: '请上传分类图标', trigger: 'blur' }],
  productUnit: [{ required: true, message: '请输入分类单位', trigger: 'blur' }]
})
/**
 * 组件挂载完成即可调用
 */
onMounted(() => {
  getCategoryList()
})
/**
 * 异步请求调用后台服务
 */
const getCategoryList = async () => {
  loading.value = true
  treeData.value = await categoryList()
  loading.value = false
}
/**
 * 新增按钮
 * @param data 当前节点数据
 */
const append = data => {
  expandedKeys.value = [data.catId]
  dialogType.value = 'add'
  titleMsg.value = '添加分类'
  //清空表单数据
  category.value = {}
  dialogVisible.value = true
  category.value.parentCid = data.catId //当前点击的层级的id
  category.value.catLevel = data.catLevel * 1 + 1 //当前层级+1
  //console.log(expandedKeys.value)
}
const edit = async data => {
  //console.log(data)//{catId: 1, name: '图书、音像、电子书刊', parentCid: 0, catLevel: 1, showStatus: 0,}
  dialogType.value = 'edit'
  titleMsg.value = '修改分类'
  dialogVisible.value = true
  //这里可以直接赋值，进行回显数据，但是以防别人已经修改了数据，但是我这里并没有刷新列表，所以这里尽量回显的时候，调用接口获取最新的数据
  //category.value = data
  category.value = await getCategory(data.catId)
}
/**
 * 提交按钮
 */
const submitData = async () => {
  if (!ruleFormRef.value) return
  try {
    // 如果校验失败，这里会 throw
    await ruleFormRef.value.validate()
    // 校验通过
    // … 发接口、关闭弹窗之类的
    console.log('校验通过，开始提交')
    if (dialogType.value == 'add') {
      addCategory()
    }
    if (dialogType.value == 'edit') {
      editCategory()
    }
  } catch (err) {
    // 校验不通过或其它错误都会走到这
    console.warn('校验失败：', err)
    ElMessage.error('请检查表单必填项或格式')
  }
}
//添加分类的方法
const addCategory = async () => {
  await insertCategory(category.value)
  ElMessage({
    type: 'success',
    message: '添加成功'
  })
  //固定当前节点展开
  expandedKeys.value = [category.value.parentCid]
  //关闭弹窗
  dialogVisible.value = false
  //刷新列表
  getCategoryList()
}
//修改分类的方法
const editCategory = async () => {
  await updateCategory(category.value)
  ElMessage({
    type: 'success',
    message: '修改成功'
  })
  //固定当前节点展开
  expandedKeys.value = [category.value.parentCid]
  //关闭弹窗
  dialogVisible.value = false
  //刷新列表
  getCategoryList()
}
/**
 * 拖拽方法 该方法返回false不能拖拽 true代表可以拖拽
 * @param dragNode 拖拽的节点
 * @param dropNode 放置的节点
 * @param dropType 放置的类型
 */
const allowDrop = (draggingNode, dropNode, type) => {
  //获取最大深度不超过4
  countNodeLevel(draggingNode.data)
  let deep = maxLevel.value - draggingNode.data.catLevel + 1
  //console.log('深度:', deep)
  //如果type == 'inner'
  if (type == 'inner') {
    return deep + dropNode.level <= 3
  } else {
    return deep + dropNode.parent.level <= 3
  }
}

//最大层数
const maxLevel = ref(3)
/**
 * 递归获取最大层级
 * @param node
 */
function countNodeLevel(node) {
  if (node.children != null && node.children.length > 0) {
    for (let i = 0; i < node.children.length; i++) {
      if (node.children[i].catLevel > maxLevel.value) {
        maxLevel.value = node.children[i].catLevel
      }
      countNodeLevel(node.children[i])
    }
  }
}
const updateNodes = ref([])
/**
 * 拖拽成功结束方法
 * @param dragNode 拖拽的节点
 * @param dropNode 目标节点
 * @param dropType 放置的类型
 */
const handleDrop = async (draggingNode, dropNode, dropType, ev) => {
  console.log('ev', ev)

  //1:当前被拖拽的节点的最新的父节点id
  let parentId = 0
  let siblings = null
  if (dropType == 'before' || dropType == 'after') {
    parentId = dropNode.parent.data.catId === undefined ? 0 : dropNode.parent.data.catId
    siblings = dropNode.parent.childNodes
  } else {
    parentId = dropNode.data.catId
    siblings = dropNode.childNodes
  }
  //2:当前被拖拽的节点的最新顺序排序
  for (let i = 0; i < siblings.length; i++) {
    if (siblings[i].data.catId == draggingNode.data.catId) {
      let catLevel = draggingNode.level
      if (siblings[i].level != draggingNode.level) {
        catLevel = siblings[i].level
        //修改子节点的层级(递归)
        updateChilderNodeLevel(siblings[i])
      }
      //如果不是兄弟元素，还需要改parentId
      updateNodes.value.push({
        catId: siblings[i].data.catId,
        sort: i,
        parentCid: parentId,
        catLevel: catLevel
      })
    } else {
      //如果是兄弟元素，只改顺序
      updateNodes.value.push({
        catId: siblings[i].data.catId,
        sort: i
      })
    }
  }
  //3:当前被拖拽的节点的最新层级
  //console.log(updateNodes.value)
  await batchUpdate(updateNodes.value)
  ElMessage({
    type: 'success',
    message: '修改成功'
  })
  //刷新列表
  getCategoryList()
  //固定当前节点展开
  expandedKeys.value = [parentId]
  updateNodes.value = []
  maxLevel.value = 0
}

/**
 * 递归修改子节点
 * @param node
 */
function updateChilderNodeLevel(node) {
  if (node.childNodes.length > 0) {
    for (let i = 0; i < node.childNodes.length; i++) {
      let cNode = node.childNodes[i].data
      updateNodes.value.push({
        catId: cNode.catId,
        catLevel: node.childNodes[i].level
      })
      updateChilderNodeLevel(node.childNodes[i])
    }
  }
}
/**
 * 删除按钮
 * @param node 当前节点信息
 * @param data 当前节点数据
 */
const remove = (node, data) => {
  ElMessageBox.confirm('你确定要删除吗?', 'Warning', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  })
    .then(async () => {
      //发送删除请求
      await removeCategory(data.catId)
      //刷新数据
      getCategoryList()
      //默认展开的节点的 key 的数组  default-expanded-keys,设置需要默认展开的节点
      expandedKeys.value = [node.parent.data.catId]
      //提示信息
      ElMessage({
        type: 'success',
        message: '删除成功'
      })
    })
    .catch(() => {
      ElMessage({
        type: 'info',
        message: '取消删除'
      })
    })
}
const categoryTree = ref(null)
const handleBatchDelete = () => {
  ElMessageBox.confirm('你确定要批量删除吗?', 'Warning', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  })
    .then(async () => {
      //获取选中的节点，不包括半选节点
      let checkedNodes = categoryTree.value.getCheckedNodes()
      //获取他们的catId主键id
      let checkNodeCatIds = checkedNodes.map(item => item.catId)
      console.log(checkNodeCatIds)
      // //发送删除请求
      await batchCategory(checkNodeCatIds)
      ElMessage({
        type: 'success',
        message: '批量删除成功'
      })
      // //刷新数据
      getCategoryList()
      // //默认展开的节点的 key 的数组  default-expanded-keys,设置需要默认展开的节点
    })
    .catch(() => {
      ElMessage({
        type: 'info',
        message: '取消删除'
      })
    })
}
</script>

<style lang="scss" scoped></style>
